%Macro GetRegNameVec(dname=,Varrange=,Type=,NVarInd=);
data &dname._sel (drop=Vname_suffix); retain CntID; set &dname;
	Cname = trim(Vname)||trim(Vname_suffix);	CntID = _n_;
	Rname = trim(Vname)||'='||trim(Cname)||';';
	F2Ename = trim(Vname)||'='||trim(Cname); 
	Mulname = trim(Vname)||'*'||trim(Cname);
	IVname = trim(Vname)||'_IV';							
	IVXname = trim(Vname)||'='||trim(Vname)||'_IV'; 		call symputx('VarNAll',_n_,'G'); *For all variables;
	IVEname = trim(Vname)||'_Even';							IVOname = trim(Vname)||'_Odd';
	IVEXname = trim(Vname)||'='||trim(IVEname);				IVOXname = trim(Vname)||'='||trim(IVOname);
	FBOLSname = trim(Vname)||'_FBOLS';						FBOLSXname = trim(Vname)||'='||trim(FBOLSname);
	FBOLSXCname = trim(Vname)||'='||trim(FBOLSname)||';'; run; 		
*Select variables from the full list;
%if %sysfunc(lowcase(&NVarInd)) = %sysfunc(lowcase(Partial)) %then %do; 
	data &dname._sel; set &dname._sel; where CntID in &Varrange; run; %end;
*For general independent variables; 
%if &type = 1 %then %do;
	data &dname._Macro; length Vnamelist $800 Cnamelist $1200 Rnamelist $2500 F2Enamelist $2500 Mulnamelist $2500 IVnamelist $800 IVXnamelist $2500 Col2Fnamelist $2500
						IVEnamelist $800 IVOnamelist $800 FBOLSnamelist $800 IVEXnamelist $2500 IVOXnamelist $2500 FBOLSXnamelist $2500 FBOLSXCnamelist $2500;
		set &dname._sel; retain Vnamelist Cnamelist Rnamelist F2Enamelist Mulnamelist IVnamelist IVXnamelist Col2Fnamelist IVEnamelist IVOnamelist IVEXnamelist IVOXnamelist FBOLSnamelist FBOLSXnamelist FBOLSXCnamelist; 
		suffix = put(_n_,5.); suffix2 = put(_n_+1,5.);
		Col2Fname = cats('Col',suffix2)||'='||trim(Vname); *Due to intercept (useful for Wide2Long);
		if _n_=1 then do; Vnamelist=Vname; Cnamelist=Cname; Rnamelist=Rname; F2Enamelist=F2Ename; Mulnamelist=Mulname; 
							IVnamelist=IVname; IVXnamelist=IVXname; Col2Fnamelist = Col2Fname; 
							IVEnamelist=IVEname; IVOnamelist = IVOname; IVEXnamelist = IVEXname; IVOXnamelist = IVOXname; 
							FBOLSnamelist=FBOLSname; FBOLSXnamelist=FBOLSXname; FBOLSXCnamelist=FBOLSXCname; end;
		else do; Vnamelist = trim(Vnamelist)||' '||trim(Vname);				Cnamelist = trim(Cnamelist)||' '||trim(Cname);
				 Rnamelist = trim(Rnamelist)||' '||trim(Rname); 			F2Enamelist = trim(F2Enamelist)||' '||trim(F2Ename);
				 Mulnamelist = trim(Mulnamelist)||'+'||trim(Mulname);		IVnamelist = trim(IVnamelist)||' '||trim(IVname);
				 IVXnamelist = trim(IVXnamelist)||' '||trim(IVXname);		Col2Fnamelist = trim(Col2Fnamelist)||' '||trim(Col2Fname); 
				 IVEnamelist = trim(IVEnamelist)||' '||trim(IVEname);		IVOnamelist = trim(IVOnamelist)||' '||trim(IVOname);
				 IVEXnamelist = trim(IVEXnamelist)||' '||trim(IVEXname);	IVOXnamelist = trim(IVOXnamelist)||' '||trim(IVOXname);
				 FBOLSnamelist = trim(FBOLSnamelist)||' '||trim(FBOLSname);	FBOLSXnamelist = trim(FBOLSXnamelist)||' '||trim(FBOLSXname); 
				 FBOLSXCnamelist = trim(FBOLSXCnamelist)||' '||trim(FBOLSXCname); end;
		* Declare global macro variables;
		call symputx('Vnamelist',Vnamelist,'G');			call symputx('Cnamelist',Cnamelist,'G');			call symputx('VarN',_n_,'G');
		call symputx('Rnamelist',Rnamelist,'G');  			call symputx(cats('Varname',suffix),Vname,'G');  	
		call symputx('F2Enamelist',F2Enamelist,'G');  		call symputx('Mulnamelist',Mulnamelist,'G');
		call symputx('IVnamelist',IVnamelist,'G');			call symputx('IVXnamelist',IVXnamelist,'G');		
		call symputx('Col2Fnamelist',Col2Fnamelist,'G');	
		call symputx('IVEnamelist',IVEnamelist,'G');		call symputx('IVOnamelist',IVOnamelist,'G');				
		call symputx('IVEXnamelist',IVEXnamelist,'G');		call symputx('IVOXnamelist',IVOXnamelist,'G');		
		call symputx('FBOLSnamelist',FBOLSnamelist,'G');	call symputx('FBOLSXnamelist',FBOLSXnamelist,'G'); 
		call symputx('FBOLSXCnamelist',FBOLSXCnamelist,'G');  run;  
%end;
*For Characteristics;
%if &type = 2 %then %do;
	data &dname._Macro; length CVnamelist $800 CCnamelist $1200 CRnamelist $2500 CComnamelist $800 Col2Cnamelist $2500;
		set &dname._sel; retain CVnamelist CCnamelist CRnamelist CComnamelist Col2Cnamelist; 
		suffix = put(_n_,5.);	suffix2 = put(_n_+1+&VarN,5.);
		Col2Cname = cats('Col',suffix2)||'='||trim(Vname); *Due to intercept and factors;
		if _n_=1 then do; CVnamelist=Vname; CCnamelist=Cname; CRnamelist=Rname; CComnamelist=Vname; Col2Cnamelist=Col2Cname; end;
		else do; CVnamelist = trim(CVnamelist)||' '||trim(Vname);			CCnamelist = trim(CCnamelist)||' '||trim(Cname);
				 CRnamelist = trim(CRnamelist)||' '||trim(Rname); 			CComnamelist = trim(CComnamelist)||','||trim(Vname); 
				 Col2Cnamelist = trim(Col2Cnamelist)||' '||trim(Col2Cname);	end;
		* Declare global macro variables;
		call symputx('CVnamelist',CVnamelist,'G');			call symputx('CCnamelist',CCnamelist,'G');		
		call symputx('CRnamelist',CRnamelist,'G');			call symputx('CComnamelist',CComnamelist,'G');  		
		call symputx('CVarN',_n_,'G');						call symputx(cats('CVarname',suffix),Vname,'G'); 
		call symputx('Col2Cnamelist',Col2Cnamelist,'G');	run;  
%end;
*For No Characteristics;
%if &type = 0 %then %do;
	%global CVnamelist CCnamelist CRnamelist CComnamelist Col2Cnamelist CVarN; 
	%let CVnamelist=; %let CCnamelist=; %let CRnamelist=; 
	%let CComnamelist=;	%let CVarN = 0;	%let Col2Cnamelist=;
%end;
%Mend GetRegNameVec;




* Macro to run TS Regressions;
%Macro RunTsReg(dname=,Xsecvname=,depvname=, depvnameXsec=, rfvname=,Regressors=,Outdname=,AdjR2=);
* Excess returns;
data &dname._tmp; set &dname; DepVarrf = &depvname - &rfvname; run;
/* proc sort data=&dname._tmp; by &Xsecvname date; run; */

* TS-regression to estimate factor loadings (betas); 
proc reg data=&dname._tmp outest=&Outdname._tmp noprint tableout;
	by &Xsecvname date; Reg: model DepVarrf = &Regressors / adjrsq b;
	title 'Time-series regression to estimate betas for a given X-sectional entity'; 
	output out=&Outdname._Resid (keep=&Xsecvname date Resid) Residual=Resid; quit;
data &Outdname._tmp (drop=_type_ _model_); set &Outdname._tmp; 
 /*  nobs=_p_ + _edf_; if nobs>=24; */
	if _type_='PARMS'; if &AdjR2 = 0 then do; drop _adjrsq_; end;
	drop _depvar_ _rmse_ DepVarrf _in_ _p_ _edf_ _rsq_; run;
*** Be careful with variable names and labels, e.g. QMktrf denotes its betas, not variable itself;
*** But want to keep the name for convenience in the following FM-Xsec Regressions;

* Merging back to the original data;
proc sql; create table &Outdname (drop=N&Xsecvname) as select *
	from &dname._tmp (keep=&Xsecvname date RankDate &depvname &depvnameXsec &rfvname &CVnamelist) as a, 
		 &Outdname._tmp (rename=(&Xsecvname=N&Xsecvname)) as b
	where a.&Xsecvname = b.N&Xsecvname and a.date=b.date; quit;
proc sort data=&Outdname (drop=intercept) nodupkey; by &Xsecvname date; run;
proc datasets lib=work; delete &dname._tmp &Outdname._tmp &Outdname._Resid; quit;
%Mend RunTsReg;


%Macro VWRank (dname=, rankdate=, rankvar=);

proc sort data=&dname;
by &rankdate;
run;

proc rank data=&dname out=&dname._tmp group=5;
by &rankdate;
var &rankvar;
ranks r_rankvar;
run;

data &dname._tmp;
set &dname._tmp;
r_rankvar=r_rankvar+1;
if missing (r_rankvar) then delete;
label r_rankvar="Factor Beta Sorted Portfolio";
run;

*Portfolio monthly return series;

proc sort data=&dname._tmp; by date r_rankvar &rankdate; run;

proc means data =&dname._tmp noprint;
   by date r_rankvar &rankdate;
   var ReturnDate/weight=Amountout; *report value-weighted using AmountOut;
   output out = &dname._port mean(ReturnDate)=ReturnDate mean(&rankvar)=&rankvar mean(&CVnamelist)=&CVnamelist;
run;


* Portfolio average monthly return;
proc sort data= &dname._port; by date r_rankvar;
   where year(date)>=1975;
run;


proc means data = &dname._port noprint;
   by date r_rankvar;
   var ReturnDate;
   output out = ewretdat  mean(ReturnDate)= ewret mean(&rankvar)=&rankvar mean(&CVnamelist)=&CVnamelist;
run;


proc sort data=ewretdat (drop=_type_ _freq_); by r_rankvar; run;

proc means data=ewretdat mean noprint; 
   class r_rankvar;
   var ewret &rankvar &CVnamelist;
   output out=portChar mean(&rankvar)=&rankvar mean(&CVnamelist)=&CVnamelist;
run;

data portChar;
set portChar;
if missing(r_rankvar) then delete;
run;

%mend VWRank;


%macro singlesort_adjupNEW(data=, factors=, byvar=, rankvar=, timevar=, lag=, var=ewret, outr=, outc=);
proc sort data=&data; by &byvar &rankvar &timevar;run;

proc transpose data=&data out=data; by &byvar &rankvar &timevar; var &var;run;
*Find H-L difference;
proc sort data=&data out=sum; by &byvar &timevar &rankvar;run;

data sum_diff; set sum; by &byvar &timevar &rankvar;
if &rankvar in (1,5); *low and high;
if &rankvar=5 then &rankvar=2; *high;
run;

proc transpose data=sum_diff out=sum_diff2; by &byvar &timevar;
  var &var; id &rankvar; run;

data sum_diff2; set sum_diff2; &rankvar=99; col1 = _2 - _1; drop _2 _1; run;

data sum_diff2; set data sum_diff2;run;
proc sort data=sum_diff2; by &byvar _name_ &rankvar;run;


**link factors to get alpha;
data ff; set &factors; 
if yyyymm>=200208;
ym=input(put(yyyymm, 6.),yymmn6.);
drop yyyymm;
format ym yymmn6.;
informat ym yymmn6.;
rename ym=date;
keep ym MKTbond rf MKTRF SMB HML UMD LIQ; 
run;

proc sql;
  create table sum_diff2 as
  select a.*, (a.col1-b.rf) as exret, b.MKTBond, b.MKTRF, b.SMB, b.HML, b.UMD, b.LIQ
  from sum_diff2 as a left join ff as b
  on a.&timevar = b.date;/* the &timevar must be consistent with the time identifier (i.e. date) from FF time-series factors*/
  quit;
proc sort data=sum_diff2; by &byvar _name_ &rankvar &timevar;
data sum_diff2; set sum_diff2; if &rankvar=99 then exret=col1; run;


***average excess return;
options nonotes;
proc model data=sum_diff2; 
 by &byvar _name_ &rankvar;
 parms a; exogenous exret ; 
 instruments / intonly;
 exret=a; 
 fit exret / gmm kernel=(bart, %eval(&lag+1), 0);
 ods output parameterestimates=param1  fitstatistics=fitresult
 OutputStatistics=residual;
 quit;

data param1; set param1; type='Eret'; if parameter='a';run;


**5-factor stock alpha;
options nonotes;
proc model data=sum_diff2; 
 by &byvar _name_ &rankvar;
 parms a b1 b2 b3 b4 b5;
 instruments MKTRF SMB HML UMD LIQ;
 exret =a+b1*MKTRF+b2*SMB+b3*HML+b4*UMD+b5*LIQ; 
 fit exret / gmm kernel=(bart, %eval(&lag+1), 0);
 ods output parameterestimates=param2  fitstatistics=fitresult
 OutputStatistics=residual;
 quit;

data param2; set param2; type='S5'; if parameter='a';run;

**1-factor bond alpha;
options nonotes;
proc model data=sum_diff2; 
 by &byvar _name_ &rankvar;
 parms a b1;
 instruments MKTbond;
 exret =a+b1*MKTbond; 
 fit exret / gmm kernel=(bart, %eval(&lag+1), 0);
 ods output parameterestimates=param3  fitstatistics=fitresult
 OutputStatistics=residual;
 quit;

data param3; set param3; type='C1'; if parameter='a';run;


**6-factor alpha;
options nonotes;
proc model data=sum_diff2; 
 by &byvar _name_ &rankvar;
 parms a b1 b2 b3 b4 b5 b6;
 instruments MKTRF SMB HML UMD LIQ MKTBond;
 exret =a+b1*MKTRF+b2*SMB+b3*HML+b4*UMD+b5*LIQ+b6*MKTBond; 
 fit exret / gmm kernel=(bart, %eval(&lag+1), 0);
 ods output parameterestimates=param4  fitstatistics=fitresult
 OutputStatistics=residual;
 quit;

data param4; set param4; type='N6'; if parameter='a';run;


data param; set param1-param4;run;

data param; set param;
 if probt<0.1 then p='*  '; if probt<0.05 then p='** ';
 if probt<0.01 then p='***'; tvalue2=put(tvalue,7.2); 
 est=put(estimate, 12.2); param=est;
 if  &rankvar=99 then PARAM=compress(est||p);
 T=compress('('||tvalue2||')');
 keep &byvar &rankvar type _name_ param T;
 rename _name_=name;run;

**output to row data;
proc sort data=param; by name &byvar type &rankvar;run;
proc transpose data=param out=&outr;
 by name &byvar type; var param T; id &rankvar; run;
 data &outr;set &outr;
 rename _99=H_L;run;
option notes;

**output to column data;
proc transpose data=param out=j; by name &byvar type &rankvar;
 var param t; run; 
data j; set j; id=weight||name||type;run; 
proc sort data=j; by &rankvar descending _name_ id;run;
proc transpose data=j out=&outc; by &rankvar descending _name_;
 var col1; id id;run;


data outcChar;
set outc portChar;
run;

%mend singlesort_adjupNEW;

